package engine.utils
{
	import ash.core.Entity;
	
	import engine.systems.motion.MotionTarget;
	import engine.systems.motion.MotionTargetNode;
	import engine.systems.motion.customMotionToTarget.CustomMotionToTarget;
	import engine.systems.motion.motion.Motion;
	import engine.systems.motion.spatialToTarget.SpatialToTarget;
	import engine.systems.motion.springToTarget.SpringToTarget;
	import engine.systems.motion.velocityToTarget.VelocityToTarget;
	import engine.systems.render.render.Render;
	import engine.systems.spatial.spatial.Spatial;
	import engine.systems.spatial.path.Path;
	import engine.systems.spatial.sleep.Sleep;
	
	import flash.geom.Point;

	public class MotionUtils
	{	
		public static function followPath(entity:Entity, points:Array, rate:Number = 1, index:int = 0, forward:Boolean = true, loop:Boolean = false, complete:Function = null, arrived:Function = null):void
		{
			MotionUtils.createMotionTargetEntity(entity, new Spatial(), null, rate, false, true, arrived);
			
			if(!entity.get(Motion))				entity.add(new Motion());
			if(!entity.get(VelocityToTarget))	entity.add(new VelocityToTarget(false));
			
			var path:Path = entity.get(Path);
			if(!path)
			{
				path = new Path();
				entity.add(path);
			}
			path.points 	= points;
			path.index 		= index;
			path.forward 	= forward;
			path.loop 		= loop;
			path.active		= true;
			if(complete != null) path.complete.addOnce(complete);
		}
		
		public static function customMotionToTarget(entity:Entity, target:Entity, handler:Function, rate:Number = 1, useContainer:Boolean = true, useSpatial:Boolean = false, arrived:Function = null):void
		{
			MotionUtils.createMotionTargetEntity(entity, target.get(Spatial), target.get(Render), rate, useContainer, useSpatial, arrived);
			
			var customMotionToTarget:CustomMotionToTarget = entity.get(CustomMotionToTarget);
			if(!customMotionToTarget)
			{
				customMotionToTarget = new CustomMotionToTarget();
				entity.add(customMotionToTarget);
			}
			customMotionToTarget.handler = handler;
		}
		
		public static function spatialToTarget(entity:Entity, target:Entity, useContainer:Boolean = true, useSpatial:Boolean = false):void
		{
			MotionUtils.createMotionTargetEntity(entity, target.get(Spatial), target.get(Render), 0, useContainer, useSpatial);
			
			if(!entity.get(SpatialToTarget)) entity.add(new SpatialToTarget());
		}
		
		public static function velocityToTarget(entity:Entity, target:Entity, ease:Boolean = true, rate:Number = 1, useContainer:Boolean = true, useSpatial:Boolean = false, arrived:Function = null):void
		{
			MotionUtils.createMotionTargetEntity(entity, target.get(Spatial), target.get(Render), rate, useContainer, useSpatial, arrived);
			
			if(!entity.get(Motion))				entity.add(new Motion());
			if(!entity.get(VelocityToTarget))	entity.add(new VelocityToTarget(ease));
		}
		
		public static function springToTarget(entity:Entity, target:Entity, dampening:Number = 0.85, rate:Number = 1, useContainer:Boolean = true, useSpatial:Boolean = false, arrived:Function = null):void
		{
			MotionUtils.createMotionTargetEntity(entity, target.get(Spatial), target.get(Render), rate, useContainer, useSpatial, arrived);
			
			if(!entity.get(Motion))	entity.add(new Motion());
			
			var springToTarget:SpringToTarget = entity.get(SpringToTarget);
			if(!springToTarget)
			{
				springToTarget = new SpringToTarget();
				entity.add(springToTarget);
			}
			springToTarget.dampening = dampening;
		}
		
		private static function createMotionTargetEntity(entity:Entity, spatial:Spatial, render:Render, rate:Number = 1, useContainer:Boolean = true, useSpatial:Boolean = false, arrived:Function = null):void
		{
			if(!entity.get(Spatial)) entity.add(new Spatial());
			
			var motionTarget:MotionTarget = entity.get(MotionTarget);
			if(!motionTarget)
			{
				motionTarget = new MotionTarget();
				entity.add(motionTarget);
			}
			motionTarget.spatial 		= spatial;
			motionTarget.render			= render;
			motionTarget.useContainer 	= useContainer;
			motionTarget.useSpatial 	= useSpatial;
			motionTarget.rate 			= rate;
			if(arrived != null) motionTarget.arrived.addOnce(arrived);
			
			var sleep:Sleep = entity.get(Sleep);
			if(sleep)
			{
				sleep.sleeping 			= false;
				sleep.ignoreOffscreen 	= true;
			}
		}
		
		public static function getTargetPoint(node:MotionTargetNode, point:Point):Point
		{
			if(node.target.useContainer && node.render && node.target.render)
			{
				MotionUtils.setTargetPoint(node.target, point, node.target.useSpatial);
				
				if(node.render.display.parent != node.target.render.display.parent)
					point = DisplayUtils.localToLocal(point.x, point.y, node.target.render.display.parent, node.render.display.parent);
			}
			else MotionUtils.setTargetPoint(node.target, point, true);
			
			return point;
		}
		
		private static function setTargetPoint(target:MotionTarget, point:Point, useSpatial:Boolean):void
		{
			if(useSpatial)
			{
				point.x = target.spatial.x;
				point.y = target.spatial.y;
			}
			else
			{
				point.x = target.render.display.x;
				point.y = target.render.display.y;
			}
		}
		
		public static function arrived(node:MotionTargetNode, atTarget:Boolean):void
		{
			if(node.target.arrived.numListeners <= 0) return;
			
			if(atTarget)
			{
				if(node.target.atTarget) return;
				node.target.atTarget = true;
				
				node.target.arrived.dispatch(node.entity);
			}
			else
			{
				if(!node.target.atTarget) return;
				node.target.atTarget = false;
			}
		}
	}
}